/*
 * Copyright (c) 2005, 2014 vacoor
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 */
package org.vacoor.nothing.web.dao.support.hbn;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Order;
import org.springframework.beans.factory.annotation.Autowired;
import org.vacoor.nothing.web.dao.CrudDao;
import org.vacoor.nothing.web.dao.support.hbn.util.Cascade;
import org.vacoor.nothing.web.domain.Filters;
import org.vacoor.nothing.web.domain.Sort;

import java.io.Serializable;
import java.util.List;
import java.util.Map;

/**
 * @author vacoor
 */
public abstract class HbnCrudDaoImpl<E, ID extends Serializable> extends HbnDaoSupport<E, ID> implements CrudDao<E, ID> {
    protected SessionFactory sessionFactory;

    @Override
    public E find(ID id) {
        return get(id);
    }

    @Override
    public E findOne(Map<String, ?> params) {
        Filters filters = getAndClearFiltersParam(params);
        Sort sort = getAndClearSortParam(params);
        Integer offset = getAndClearOffsetParam(params);
        Integer maxResults = getAndClearMaxResultsParam(params);
        DetachedCriteria dc = buildDetachedCriteria();

        Cascade.from(dc).allEq(params).filter(filters).sort(sort).over();

        offset = offset != null ? offset : 0;
        maxResults = maxResults != null ? maxResults : Integer.MAX_VALUE;

        return findOne(dc, new Order[0], offset, maxResults);
    }

    @Override
    public List<E> findMany(Map<String, ?> params) {
        Filters filters = getAndClearFiltersParam(params);
        Sort sort = getAndClearSortParam(params);
        Integer offset = getAndClearOffsetParam(params);
        Integer maxResults = getAndClearMaxResultsParam(params);
        DetachedCriteria dc = buildDetachedCriteria();

        Cascade.from(dc).allEq(params).filter(filters).sort(sort).over();

        offset = offset != null ? offset : 0;
        maxResults = maxResults != null ? maxResults : Integer.MAX_VALUE;

        return findMany(dc, new Order[0], offset, maxResults);
    }

    @Override
    public int count(Map<String, ?> params) {
        Filters filters = getAndClearFiltersParam(params);
        DetachedCriteria dc = buildDetachedCriteria();

        Cascade.from(dc).allEq(params).filter(filters).over();

        return count(dc);
    }

    @Override
    public void save(E e) {
        super.save(e);
    }

    @Override
    public void update(E e) {
        super.update(e);
    }

    @Override
    public void delete(E e) {
        super.delete(e);
    }

    /*- 删除应该对象前应该先判断该对象是否存在, 给出合理的提示, 而不应该直接由数据库决定
    @Override
    public void delete(ID id) {
        E e = find(id);
        if (e != null) {
            delete(e);
        }
    }

    @Override
    public void deleteBy(Map<String, ?> params) {
    */
        /*- HQL 级联无效
        StringBuilder buff = new StringBuilder();

        buff.append("DELETE FROM ").append(getEntityName())
                .append(" WHERE 1 = 1");

        for (String prop : params.keySet()) {
            buff.append(" AND ").append(prop).append(" = :").append(prop);
        }

        executeUpdate(buff.toString(), params);
        */
    /*
        List<E> many = findMany(params);
        for (E one : many) {
            delete(one);
        }
    }
    */

    /* ******************
     *   特殊参数处理
     * ******************/

    protected Integer getAndClearOffsetParam(Map<String, ?> params) {
        Object offset = params.remove(OFFSET_PROP);
        if (offset != null && (!(offset instanceof Integer))) {
            throw new IllegalArgumentException(String.format("OFFSET_PROP (%s) must be is integer", OFFSET_PROP));
        }
        return (Integer) offset;
    }

    protected Integer getAndClearMaxResultsParam(Map<String, ?> params) {
        Object maxResults = params.remove(MAX_RESULTS_PROP);
        if (maxResults != null && (!(maxResults instanceof Integer))) {
            throw new IllegalArgumentException(String.format("MAX_RESULTS (%s) must be is integer", MAX_RESULTS_PROP));
        }
        return (Integer) maxResults;
    }

    protected Filters getAndClearFiltersParam(Map<String, ?> params) {
        Object keywords = params.remove(FILTERS_PROP);
        if (keywords != null && (!(keywords instanceof Filters))) {
            throw new IllegalArgumentException(String.format("FILTERS_PROP (%s) must be is %s's instance", FILTERS_PROP, Filters.class.getName()));
        }
        return (Filters) keywords;
    }

    protected Sort getAndClearSortParam(Map<String, ?> params) {
        Object sort = params.remove(SORT_PROP);
        if (sort != null && (!(sort instanceof Sort))) {
            throw new IllegalArgumentException(String.format("SORT_PROP (%s) must be is %s's instance", SORT_PROP, Sort.class.getName()));
        }
        return (Sort) sort;
    }
    /* ******************
     *
     * ******************/

    public SessionFactory getSessionFactory() {
        return sessionFactory;
    }

    public Session getSession() {
        return getSessionFactory().getCurrentSession();
    }

    protected String getEntityName() {
        return getSessionFactory().getClassMetadata(entityType).getEntityName();
    }

    protected String getIdentifierName() {
        return getSessionFactory().getClassMetadata(entityType).getIdentifierPropertyName();
    }

    @Autowired
    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }
}
